import io from 'socket.io-client';
import { SOCKET_IO_PATH } from '@util/constants';

class SocketIO {
  static checkFilter = (filter, data) => {
    if (typeof filter === 'object') {
      const keys = Object.keys(filter);
      const passed = keys.every(key => {
        const items = data[key];
        if (!items) {
          return false;
        }
        const validateValue = filter[key];
        if (Array.isArray(items)) {
          return items.indexOf(validateValue) !== -1;
        } else {
          return items === validateValue;
        }
      });

      return passed;
    }

    if (typeof filter === 'function') {
      return Boolean(filter(data));
    }

    return true;
  };

  constructor(options = {}) {
    const { token, userId } = options;
    if (!token) {
      return null;
    }
    this.token = token;
    this.userId = userId;
    this.socketIO = null;

    /**
     * 监听者
     * {
     *    [type]: [{
     *        filter: object,
     *        listener: function
     *    }]
     * }
     */
    this.listeners = {};
    this.initSocket();
  }

  joinRooms = () => {
    this.send({
      // eslint-disable-next-line
      handler_name: 'task_handler',
      method: 'join_room'
    });
    this.send({
      // eslint-disable-next-line
      handler_name: 'notice_handler',
      method: 'join_room'
    });
  };

  /**
   * @param {string} type 监听的事件类型 handler_name or handler_name.method
   * @param {object} filter 过滤条件
   * @param {function} listener 监听事件
   */
  on = (type, listener, filter = {}) => {
    if (!this.listeners[type]) {
      this.listeners[type] = [];
    }

    if (this.listeners[type].every(lis => lis !== listener)) {
      this.listeners[type].push({ filter, listener });
    }
  };

  cancel = (type, listener) => {
    if (!listener) {
      this.listeners[type] = [];
    }
    const subListeners = this.listeners[type];
    if (!Array.isArray(subListeners)) {
      return;
    }
    const index = subListeners.findIndex(sl => sl && sl.listener === listener);
    if (index !== -1) {
      subListeners.splice(index, 1);
    }
  };

  emit = (type, data, method, handlerName) => {
    const subListeners = this.listeners[type];
    if (Array.isArray(subListeners)) {
      subListeners.forEach(({ filter, listener }) => {
        if (SocketIO.checkFilter(filter, data)) {
          listener(data, method, handlerName);
        }
      });
    }
  };

  send = data => {
    if (!this.socketIO || !this.token) {
      return false;
    }
    let sent = {};
    if (data instanceof Object) {
      sent = data;
    } else {
      sent.data = data;
    }
    sent.token = this.token;
    // eslint-disable-next-line
    sent.user_id = this.userId;
    this.socketIO.send(JSON.stringify(sent));
    return true;
  };

  initSocket = () => {
    const socket = io({
      path: SOCKET_IO_PATH,
      query: { name: 'browser' }
    });
    socket.on('disconnect', reason => {
      window.console.log('socket.io 断开连接');
      if (reason === 'io server disconnect') {
        window.console.log('socket.io 尝试重连');
        socket.connect();
        this.joinRooms();
      }
    });
    socket.on('connect', () => {
      window.console.log('socket.io 连接成功');
      this.joinRooms();
    });
    socket.on('message', (msg, callback) => {
      if (typeof callback === 'function') {
        callback('ok');
      }
      if (ENVIRONMENT === 'dev' && !window.DISABLE_SOCKET_IO_MSG_LOG) {
        window.console.log(msg);
      }
      this.handleMsg(msg);
    });
    this.socketIO = socket;
  };

  handleMsg = msg => {
    if (typeof msg !== 'object') {
      return;
    }
    try {
      // eslint-disable-next-line
      const { handler_name: handlerName, method, data } = msg;
      this.emit(handlerName, data, method);
      this.emit(`${handlerName}.${method}`, data);
      if (!handlerName && !method) {
        // 迷失消息
        this.emit('strayed', data, method, handlerName);
      }
    } catch (error) {
      window.console.log(error);
    }
  };
}

export default SocketIO;
